package com.chenliang.swagger.config;



import com.chenliang.swagger.service.WordService;
import com.chenliang.swagger.service.impl.WordServiceImpl;
import com.chenliang.swagger.utils.JsonUtils;
import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.resource.PathResourceResolver;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
import org.springframework.web.util.UrlPathHelper;
import springfox.documentation.RequestHandler;
import springfox.documentation.annotations.ApiIgnore;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.DocumentationCache;
import springfox.documentation.spring.web.json.Json;
import springfox.documentation.spring.web.json.JsonSerializer;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.ApiResourceController;
import springfox.documentation.swagger.web.SecurityConfiguration;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper;
import springfox.documentation.swagger2.web.Swagger2Controller;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @program: myes
 * @description: 针对 swagger的配置,提供swagger访问入口
 * @author: 陈英俊
 * @create: 2021-09-09 10:22
 */

@Configuration  // 表明是一个配置类
@EnableSwaggerBootstrapUI
@EnableSwagger2 // UI和2在一起成为增强注释
@ConditionalOnProperty(
        prefix = "config",
        name = {"swagger.open"},
        havingValue = "true",
        matchIfMissing = true
)
public class SwaggerConfiguration {

    /**
     * swagger默认扫描的到接口的路径
     */
    @Value("${config.swagger.selectPackage:com.chenliang.core.biz}")
    private String selectPackage;

    /**
     * 标题
     */
    @Value("${config.swagger.title:ChenLiang Doc}")
    private String title;
    /**
     * 说明
     */
    @Value("${config.swagger.description:ChenLiang Api文档}")
    private String description;

    /**
     * 配置服务URL
     */
    @Value("${config.swagger.termsOfServiceUrl:服务器URL暂时没有}")
    private String termsOfServiceUrl;
    /**
     * 配置版本
     */
    @Value("${config.swagger.version:1.0}")
    private String version;

    /**
     * 作者
     */
    @Value("${config.swagger.contact:ChenLiang}")
    private String contact;

    public SwaggerConfiguration() {
    }

    @Bean
    public Docket createRestApi() {
        // 条件表达式 ? 表达式1: 表达式2
        return StringUtils.isNotBlank(this.selectPackage)
                ? (new Docket(DocumentationType.SWAGGER_2)).apiInfo(this.apiInfo())// 设置api的元信息
                .select().apis(basePackage(this.selectPackage)).paths(PathSelectors.any()).build()
                : (new Docket(DocumentationType.SWAGGER_2)).apiInfo(this.apiInfo())
                .select().apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)).paths(PathSelectors.any()).build();
    }

    @Bean({"wordService"})
    public WordService createWordService() {
        return new WordServiceImpl();
    }

    public static Predicate<RequestHandler> basePackage(final String basePackage) {
        return new Predicate<RequestHandler>() {
            public boolean apply(RequestHandler input) {
                return SwaggerConfiguration.declaringClass(input).transform(SwaggerConfiguration.handlerPackage(basePackage)).or(true);
            }
        };
    }

    private static Function<Class<?>, Boolean> handlerPackage(final String basePackage) {
        return new Function<Class<?>, Boolean>() {
            public Boolean apply(Class<?> input) {
                String[] var2 = basePackage.split(",");
                int var3 = var2.length;

                for (int var4 = 0; var4 < var3; ++var4) {
                    String strPackage = var2[var4];
                    boolean isMatch = input.getPackage().getName().startsWith(strPackage);
                    if (isMatch) {
                        return true;
                    }
                }

                return false;
            }
        };
    }

    private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
        return Optional.fromNullable(input.declaringClass());
    }

    private ApiInfo apiInfo() {
        return (new ApiInfoBuilder()).title(this.title).description(this.description).termsOfServiceUrl(this.termsOfServiceUrl).version(this.version).contact(new Contact(this.contact, "", "")).build();
    }

    @Bean
    public SimpleUrlHandlerMapping swaggerUrlHandlerMapping(ServletContext servletContext, @Value("${swagger.mapping.order:10}") int order) {
        SimpleUrlHandlerMapping urlHandlerMapping = new SimpleUrlHandlerMapping();
        Map<String, ResourceHttpRequestHandler> urlMap = new HashMap();
        this.resolver(urlMap, servletContext, "META-INF/resources/webjars/", "/webjars/**");
        this.resolver(urlMap, servletContext, "META-INF/resources/", "/**");
        urlHandlerMapping.setUrlMap(urlMap);
        urlHandlerMapping.setOrder(order);
        return urlHandlerMapping;
    }

    private void resolver(Map<String, ResourceHttpRequestHandler> urlMap, ServletContext servletContext, String resouce, String path) {
        try {
            PathResourceResolver pathResourceResolver = new PathResourceResolver();
            pathResourceResolver.setAllowedLocations(new ClassPathResource(resouce));
            pathResourceResolver.setUrlPathHelper(new UrlPathHelper());
            ResourceHttpRequestHandler resourceHttpRequestHandler = new ResourceHttpRequestHandler();
            resourceHttpRequestHandler.setLocations(Arrays.asList(new ClassPathResource(resouce)));
            resourceHttpRequestHandler.setResourceResolvers(Arrays.asList(pathResourceResolver));
            resourceHttpRequestHandler.setServletContext(servletContext);
            resourceHttpRequestHandler.afterPropertiesSet();
            urlMap.put("/swagger" + path, resourceHttpRequestHandler);
        } catch (Exception var7) {
            // this.logger.error(var7.getMessage(), var7);
            System.out.println(var7);
        }

    }

    @ConditionalOnProperty(
            prefix = "cl",
            name = {"swagger.open"},
            havingValue = "true",
            matchIfMissing = true
    )
    @Controller
    @ApiIgnore
    @RequestMapping({"/swagger"})
    public static class SwaggerResourceController implements InitializingBean {
        @Autowired
        private ApiResourceController apiResourceController;
        @Autowired
        private Environment environment;
        @Autowired
        private DocumentationCache documentationCache;
        @Autowired
        private ServiceModelToSwagger2Mapper mapper;
        @Autowired
        private JsonSerializer jsonSerializer;
        private Swagger2Controller swagger2Controller;
        @Autowired
        private WordService wordService;
        @Autowired
        private RestTemplate restTemplate;
        @Value("${config.swagger.api-url:}")
        private String swaggerUrl;
        @Value("${server.port:8080}")
        private Integer port;

        public SwaggerResourceController() {
        }

        public void afterPropertiesSet() {
            this.swagger2Controller = new Swagger2Controller(this.environment, this.documentationCache, this.mapper, this.jsonSerializer);
        }

        @RequestMapping
        public ModelAndView index() {
            return new ModelAndView("redirect://swagger/swagger-ui.html");
        }

        @RequestMapping({"/swagger-resources/configuration/security"})
        @ResponseBody
        public ResponseEntity<SecurityConfiguration> securityConfiguration() {
            return this.apiResourceController.securityConfiguration();
        }

        @RequestMapping({"/swagger-resources/configuration/ui"})
        @ResponseBody
        public ResponseEntity<UiConfiguration> uiConfiguration() {
            return this.apiResourceController.uiConfiguration();
        }

        @RequestMapping({"/swagger-resources"})
        @ResponseBody
        public ResponseEntity<List<SwaggerResource>> swaggerResources() {
            return this.apiResourceController.swaggerResources();
        }

        @RequestMapping(
                value = {"/v2/api-docs"},
                method = {RequestMethod.GET},
                produces = {"application/json", "application/hal+json"}
        )
        @ResponseBody
        public ResponseEntity<Json> getDocumentation(@RequestParam(value = "group", required = false) String swaggerGroup, HttpServletRequest servletRequest) {
            return this.swagger2Controller.getDocumentation(swaggerGroup, servletRequest);
        }

        /**
         * @deprecated
         */
        @Deprecated
        @RequestMapping({"/toWord"})
        public String getWord(Model model, @RequestParam(value = "url", required = false) String url, @RequestParam(value = "download", required = false, defaultValue = "1") Integer download) {
            url = StringUtils.defaultIfBlank(url, this.swaggerUrl);
            Map<String, Object> result = this.wordService.tableList(url);
            model.addAttribute("url", url);
            model.addAttribute("download", download);
            model.addAllAttributes(result);
            return "word";
        }

        /**
         * @deprecated
         */
        @Deprecated
        @RequestMapping({"/toWord1"})
        public String getWord1(Model model, @RequestParam(value = "url", required = false) String url, @RequestParam(value = "download", required = false, defaultValue = "1") Integer download) {
            url = StringUtils.defaultIfBlank(url, this.swaggerUrl);
            Map<String, Object> result = this.wordService.tableList(url);
            return JsonUtils.encode(result);
        }

        @RequestMapping({"/downloadWord"})
        public void word(@RequestParam(required = false) String url, HttpServletRequest request, HttpServletResponse response) {
            ResponseEntity<String> forEntity = this.restTemplate.getForEntity("http://localhost:" + this.port + request.getContextPath() + "/swagger/toWord?download=0&url=" + StringUtils.defaultIfBlank(url, this.swaggerUrl), String.class);
            response.setContentType("application/octet-stream;charset=utf-8");
            response.setCharacterEncoding("utf-8");

            try {
                BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
                Throwable var6 = null;

                try {
                    response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode("toWord.doc", "utf-8"));
                    byte[] bytes = forEntity.getBody().getBytes();
                    bos.write(bytes, 0, bytes.length);
                    bos.flush();
                } catch (Throwable var16) {
                    var6 = var16;
                    throw var16;
                } finally {
                    if (bos != null) {
                        if (var6 != null) {
                            try {
                                bos.close();
                            } catch (Throwable var15) {
                                var6.addSuppressed(var15);
                            }
                        } else {
                            bos.close();
                        }
                    }

                }
            } catch (IOException var18) {
                var18.printStackTrace();
            }

        }
    }
}
